<?php
/**
 * SocialEngine
 *
 * @category   Application_Core
 * @package    Storage
 * @copyright  Copyright 2006-2010 Webligo Developments
 * @license    http://www.socialengine.net/license/
 * @version    $Id: Mirrored.php 8427 2011-02-09 23:11:24Z john $
 * @author     John Boehr <j@webligo.com>
 */

/**
 * @category   Application_Core
 * @package    Storage
 * @copyright  Copyright 2006-2010 Webligo Developments
 * @license    http://www.socialengine.net/license/
 */
class Storage_Service_Mirrored extends Storage_Service_Abstract {
	protected $_type = 'mirrored';
	
	protected $_services;
	
	protected $_mirrorImmediately = true;
	
	public function __construct(array $config = array()) {
		// Get services
		if (! empty ( $config ['services'] )) {
			$this->_services = $config ['services'];
		} else {
			$serviceTable = Engine_Api::_ ()->getDbtable ( 'services', 'storage' );
			$this->_services = $serviceTable->select ()->from ( $serviceTable, 'service_id' )->where ( 'enabled = ?', true )->query ()->fetchAll ( Zend_Db::FETCH_COLUMN );
		}
		// Do not allow self
		$this->_services = array_diff ( $this->_services, ( array ) $config ['service_id'] );
		// Whoops, no services
		if (empty ( $this->_services )) {
			throw new Storage_Service_Exception ( 'No services available.' );
		}
		
		parent::__construct ( $config );
	}
	
	public function getType() {
		return $this->_type;
	}
	
	/**
	 * Returns a url that allows for external access to the file. May point to some
	 * adapter which then retrieves the file and outputs it, if desirable
	 *
	 * @param Storage_Model_DbRow_File The file for operation
	 * @return string
	 */
	public function map(Storage_Model_File $model) {
		// Get service from mirrors
		$mirrors = $this->_getFileMirrors ( $model );
		$index = array_rand ( $mirrors );
		$service_id = $mirrors [$index];
		$serviceTable = Engine_Api::_ ()->getDbtable ( 'services', 'storage' );
		$otherService = $serviceTable->getService ( $service_id );
		
		return $otherService->map ( $model );
	}
	
	/**
	 * Stores a local file in the storage service
	 *
	 * @param Zend_Form_Element_File|array|string $file Temporary local file to store
	 * @param array $params Contains iden
	 * @return string Storage type specific path (internal use only)
	 */
	public function store(Storage_Model_File $model, $file) {
		if (count ( $this->_services ) <= 0) {
			throw new Storage_Service_Exception ( 'Unable to get storage service.' );
		}
		
		$serviceTable = Engine_Api::_ ()->getDbtable ( 'services', 'storage' );
		$mirrorsTable = Engine_Api::_ ()->getDbtable ( 'mirrors', 'storage' );
		$path = null;
		
		if ($this->_mirrorImmediately) {
			
			foreach ( $this->_services as $service_id ) {
				$otherService = $serviceTable->getService ( $service_id );
				$path = $otherService->store ( $model, $file );
				
				// Save to mirrors
				$mirrorsTable->insert ( array ('file_id' => $model->file_id, 'service_id' => $service_id ) );
			}
		
		} else {
			
			do {
				$index = $this->_getCounter () % count ( $this->_services );
			} while ( ! isset ( $this->_services [$index] ) );
			$service_id = $this->_services [$index];
			
			// Get the other serice
			$otherService = $serviceTable->getService ( $service_id );
			
			$path = $otherService->store ( $model, $file );
			
			// Save to mirrors
			$mirrorsTable->insert ( array ('file_id' => $model->file_id, 'service_id' => $service_id ) );
		
		}
		
		return $path;
	}
	
	/**
	 * Returns the content of the file
	 *
	 * @param Storage_Model_DbRow_File $model The file for operation
	 * @param array $params
	 */
	public function read(Storage_Model_File $model) {
		// Get service from mirrors
		$mirrors = $this->_getFileMirrors ( $model );
		$index = array_rand ( $mirrors );
		$service_id = $mirrors [$index];
		$serviceTable = Engine_Api::_ ()->getDbtable ( 'services', 'storage' );
		$otherService = $serviceTable->getService ( $service_id );
		
		return $otherService->read ( $model );
	}
	
	/**
	 * Creates a new file from data rather than an existing file
	 *
	 * @param Storage_Model_DbRow_File $model The file for operation
	 * @param string $data
	 */
	public function write(Storage_Model_File $model, $data) {
		if (count ( $this->_services ) <= 0) {
			throw new Storage_Service_Exception ( 'Unable to get storage service.' );
		}
		
		$serviceTable = Engine_Api::_ ()->getDbtable ( 'services', 'storage' );
		$mirrorsTable = Engine_Api::_ ()->getDbtable ( 'mirrors', 'storage' );
		$path = null;
		
		if ($this->_mirrorImmediately) {
			
			foreach ( $this->_services as $service_id ) {
				$otherService = $serviceTable->getService ( $service_id );
				$path = $otherService->store ( $model, $file );
				
				// Save to mirrors
				$mirrorsTable->insert ( array ('file_id' => $model->file_id, 'service_id' => $service_id ) );
			}
		
		} else {
			
			do {
				$index = $this->_getCounter () % count ( $this->_services );
			} while ( ! isset ( $this->_services [$index] ) );
			$service_id = $this->_services [$index];
			
			// Get the other serice
			$otherService = $serviceTable->getService ( $service_id );
			
			$path = $otherService->write ( $model, $data );
			
			// Save to mirrors
			$mirrorsTable->insert ( array ('file_id' => $model->file_id, 'service_id' => $service_id ) );
		
		}
		
		return $path;
	}
	
	/**
	 * Removes the file
	 *
	 * @param Storage_Model_DbRow_File $model The file for operation
	 */
	public function remove(Storage_Model_File $model) {
		if (! empty ( $model->storage_path )) {
			$serviceTable = Engine_Api::_ ()->getDbtable ( 'services', 'storage' );
			$mirrors = $this->_getFileMirrors ( $model );
			foreach ( $mirrors as $mirrorServiceId ) {
				$otherService = $serviceTable->getService ( $mirrorServiceId );
				$otherService->remove ( $model );
			}
		}
	}
	
	/**
	 * Creates a local temporary local copy of the file
	 *
	 * @param Storage_Model_DbRow_File $model The file for operation
	 */
	public function temporary(Storage_Model_File $model) {
		// Get service from mirrors
		$mirrors = $this->_getFileMirrors ( $model );
		$index = array_rand ( $mirrors );
		$service_id = $mirrors [$index];
		$serviceTable = Engine_Api::_ ()->getDbtable ( 'services', 'storage' );
		$otherService = $serviceTable->getService ( $service_id );
		
		return $otherService->temporary ( $model );
	}
	
	public function removeFile($path) {
		$serviceTable = Engine_Api::_ ()->getDbtable ( 'services', 'storage' );
		$mirrors = $this->_getFileMirrors ( $model );
		foreach ( $mirrors as $mirrorServiceId ) {
			$otherService = $serviceTable->getService ( $mirrorServiceId );
			$otherService->removeFile ( $path );
		}
	}
	
	// Utility
	

	protected function _getCounter($increment = true) {
		$settings = Engine_Api::_ ()->getApi ( 'settings', 'core' );
		$counter = $settings->getSetting ( 'storage.service.mirrored.counter', 0 );
		if ($increment) {
			$counter ++;
			$settings->setSetting ( 'storage.service.mirrored.counter', $counter );
		}
		return $counter;
	}
	
	protected function _getFileMirrors(Storage_Model_File $file) {
		$mirrorsTable = Engine_Api::_ ()->getDbtable ( 'mirrors', 'storage' );
		return $mirrorsTable->select ()->from ( $mirrorsTable, 'service_id' )->where ( 'file_id = ?', $file->file_id )->query ()->fetchAll ( Zend_Db::FETCH_COLUMN );
	}
}